package server

import (
	"context"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware/logging"
	"github.com/go-kratos/kratos/v2/middleware/recovery"
	"github.com/go-kratos/kratos/v2/middleware/selector"
	"github.com/go-kratos/kratos/v2/middleware/validate"
	"github.com/go-kratos/kratos/v2/transport/http"
	"github.com/gorilla/handlers"
	v1 "kratos-realworld/api/realworld/v1"
	"kratos-realworld/internal/conf"
	"kratos-realworld/internal/pkg/middleware/auth"
	"kratos-realworld/internal/service"
	nethttp "net/http"
)

// 定制中间件是通过 operation 匹配，而不是 http 本身的路由
// operation 是 http 及 grpc 统一的 grpc path
// grpc path 的拼接规则为: /包名.服务名/方法名
func NewSkipRoutersMatcher() selector.MatchFunc {
	skipRouters := map[string]struct{}{
		"/realworld.v1.RealWorld/Login":    {},
		"/realworld.v1.RealWorld/Register": {},
	}
	return func(ctx context.Context, operation string) bool {
		if _, ok := skipRouters[operation]; ok {
			return false
		}
		return true
	}
}

// NewHTTPServer new a HTTP server.
func NewHTTPServer(c *conf.Server, jc *conf.Jwt, service *service.RealWorldService, logger log.Logger) *http.Server {
	var opts = []http.ServerOption{
		http.ErrorEncoder(errorEncoder),
		http.Middleware(
			recovery.Recovery(),
			// 登录认证
			selector.Server(auth.JwtAuth(jc.Secret)).Match(NewSkipRoutersMatcher()).Build(),
			// 参数校验
			validate.Validator(),
			// 记录日志
			logging.Server(logger),
		),
		http.Filter(
			func(h nethttp.Handler) nethttp.Handler {
				return nethttp.HandlerFunc(
					func(w nethttp.ResponseWriter, r *nethttp.Request) {
						fmt.Println("router filter in")
						h.ServeHTTP(w, r)
						fmt.Println("router filter out")
					},
				)
			},
			handlers.CORS(
				handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}),
				handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"}),
				handlers.AllowedOrigins([]string{"*"}),
			),
		),
	}
	if c.Http.Network != "" {
		opts = append(opts, http.Network(c.Http.Network))
	}
	if c.Http.Addr != "" {
		opts = append(opts, http.Address(c.Http.Addr))
	}
	if c.Http.Timeout != nil {
		opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
	}
	srv := http.NewServer(opts...)
	v1.RegisterRealWorldHTTPServer(srv, service)
	return srv
}
